import { CzmCustomPrimitive, CzmTexture, ESCesiumViewer } from "@czmSrc/index";
import { computeCzmModelMatrix, createInnerClassFromJson } from "@czmSrc/utils";
import { createNextAnimateFrameEvent, Destroyable, extendClassProps, react, reactArray, reactArrayWithUndefined, ReactivePropsToNativePropsAndChanged, track } from "xbsj-base";
import * as Cesium from 'cesium';
import { ESJNativeNumber16 } from "earthsdk3";
/**
 * 圆圈实现，用于旋转编辑
 */
export class GeoRotatorCircle extends Destroyable {
    static defaults = {
        // 属性的类型若存在undefined的情况，这里配置为undefined时应该使用的默认值
        position: [116.39, 39.9, 100] as [number, number, number],
    }
    private _czmTexture?: CzmTexture;
    get czmTexture() { return this._czmTexture; }

    private _customPrimitiveForCircle?: CzmCustomPrimitive;
    get customPrimitiveForCircle() { return this._customPrimitiveForCircle; }

    private _circleCanvas?: CanvasRenderingContext2D;
    get circleCanvas() { return this._circleCanvas; }

    private _debugAxis?: CzmCustomPrimitive;
    get debugAxis() { return this._debugAxis; }

    constructor(czmViewer: ESCesiumViewer) {
        super();
        const viewer = czmViewer.viewer;
        if (!viewer) return;
        this._czmTexture = this.disposeVar(new CzmTexture(czmViewer))
        this._czmTexture.uri = ''; // 不能是空字符串，否则绘制创建白纹理！
        this._createCircleCanvas(czmViewer);
        this._createCustomPrimitiveForCircle(czmViewer);
        this._createDebugAxis(czmViewer);
    }
    private _createCircleCanvas(czmViewer: ESCesiumViewer) {
        let flipText = this.disposeVar(react(false));
        const canvas = document.createElement('canvas');
        canvas.width = canvas.height = 256;
        this._circleCanvas = canvas.getContext('2d') as CanvasRenderingContext2D;
        {
            const baseRotation = 0;
            // 更新纹理
            const updateTexture = () => {
                const info = {
                    rotation: this.circleRotation * Math.PI / 180,
                    startRotation: this.circleStartRotation * Math.PI / 180,
                    endRotation: this.circleEndRotation * Math.PI / 180,
                }

                const ctx = this._circleCanvas as CanvasRenderingContext2D;
                const color = this.color;
                const colorStr = `rgba(${color[0] * 255 | 0}, ${color[1] * 255 | 0}, ${color[2] * 255 | 0}, ${color[3]})`
                ctx.clearRect(0, 0, 256, 256);//清空画布

                // 绘制外圈
                ctx.lineWidth = 6;
                ctx.beginPath();
                ctx.arc(128, 128, 64, 0, Math.PI * 2, false); //绘制一个圆形
                ctx.strokeStyle = `rgba(0,0,0,${color[3]})`;
                ctx.stroke();

                // 绘制内圈
                ctx.lineWidth = 4;
                ctx.beginPath();
                ctx.arc(128, 128, 64, 0, Math.PI * 2, false); //绘制一个圆形,叠加在上一个圈上
                ctx.strokeStyle = colorStr;
                ctx.stroke();
                {
                    // 绘制旋转线
                    ctx.lineWidth = 1;
                    ctx.beginPath();
                    let startRotation = Cesium.Math.zeroToTwoPi(info.startRotation);
                    let endRotation = Cesium.Math.zeroToTwoPi(info.endRotation);
                    if (endRotation < startRotation)
                        endRotation += Cesium.Math.TWO_PI

                    if (startRotation !== endRotation || startRotation !== 0) {
                        ctx.moveTo(128, 128);
                        ctx.arc(128, 128, 64, startRotation, endRotation, (endRotation - startRotation) > Math.PI);
                        ctx.lineTo(128, 128);
                        ctx.strokeStyle = 'rgba(255, 255, 0, 1.0)';
                        ctx.stroke();
                    }
                }
                let rotation = Cesium.Math.negativePiToPi(info.rotation);
                {
                    // 绘制旋转扇区
                    ctx.lineWidth = 3;
                    ctx.beginPath();
                    ctx.moveTo(128, 128);
                    ctx.arc(128, 128, 64, baseRotation, rotation, Cesium.Math.negativePiToPi(rotation - baseRotation) < 0);
                    ctx.lineTo(128, 128);
                    ctx.fillStyle = `rgba(${color[0] * 255 | 0}, ${color[1] * 255 | 0}, ${color[2] * 255 | 0}, 0.5)`;
                    ctx.fill();
                }
                {
                    // 绘制文字
                    ctx.save();
                    ctx.font = "16px console";
                    ctx.textBaseline = "middle";
                    const rotationOnCircle = Cesium.Math.negativePiToPi(rotation - baseRotation);
                    const angle = (rotationOnCircle * 180 / Math.PI * 10 | 0) / 10;
                    const angleStr = `${angle}°`;
                    var width = ctx.measureText(angleStr).width; // TextMetrics object
                    ctx.translate(128, 128);
                    ctx.rotate(Math.PI * 0.5);
                    // ctx.rotate(rotation * 0.5);
                    // baseRotation为pi/2时，以下旋转有一部分不是最短圆弧的中心点
                    // ctx.rotate((rotation + baseRotation) * 0.5);
                    // 需要先把rotation转到-90-270，这样+90再除2就刚好落在最短圆弧中心点
                    rotation = Cesium.Math.negativePiToPi(rotation - baseRotation) + baseRotation; // 对于baseRotation为90时，范围在-90-270；对于baserotation为0时，范围在-180-180
                    ctx.rotate((rotation + baseRotation) * 0.5);
                    ctx.scale(!flipText.value ? 1 : -1, 1);
                    ctx.translate(-128, -128);
                    ctx.fillStyle = 'white';
                    ctx.fillText(angleStr, 128 - width * 0.5, 128 - 64 - 10);
                    ctx.lineWidth = 1;
                    // ctx.strokeStyle = 'black';
                    // ctx.strokeText(angleStr, 128-width*0.5, 128-64-10);
                    ctx.restore();
                }
                this.czmTexture && this.czmTexture.copyFromCanvas(canvas);
            }
            updateTexture();
            const event = this.ad(createNextAnimateFrameEvent(
                this.colorChanged,
                this.circleRotationChanged,
                this.circleStartRotationChanged,
                this.circleEndRotationChanged,
                flipText.changed
            ));
            this.ad(event.don(updateTexture));
            this.czmTexture && this.ad(this.czmTexture.readyEvent.don(updateTexture));
        }
        {
            const update = () => {
                const rm = computeCzmModelMatrix({
                    rotation: this.selfRotation,
                });
                if (!rm) return;
                const m = computeCzmModelMatrix({
                    position: this.position,
                    rotation: this.rotation,
                    localModelMatrix: Cesium.Matrix4.toArray(rm) as ESJNativeNumber16,
                })
                if (!m) return;
                const { viewer } = czmViewer;
                if (!viewer) return;
                const dot = Cesium.Cartesian3.dot(viewer.scene.camera.directionWC, new Cesium.Cartesian3(m[8], m[9], m[10]));
                flipText.value = dot > 0;
            }
            update();
            const event = this.ad(createNextAnimateFrameEvent(
                czmViewer.cameraChanged,
                this.positionChanged,
                this.rotationChanged,
                this.selfRotationChanged,
            ))
            this.ad(event.don(update));
        }
    }
    private _createCustomPrimitiveForCircle(czmViewer: ESCesiumViewer) {
        this._customPrimitiveForCircle = this.disposeVar(createCustomPrimitive(czmViewer));
        {
            this.dispose(track([this._customPrimitiveForCircle, 'show'], [this, 'show']));
            this.dispose(track([this._customPrimitiveForCircle, 'position'], [this, 'position']));
            this.dispose(track([this._customPrimitiveForCircle, 'rotation'], [this, 'rotation']));
            this.dispose(track([this._customPrimitiveForCircle, 'pixelSize'], [this, 'pixelSize']));
        }
        {
            // rotation 属性用来做第二次旋转，所以只能用localModelMatrix来赋值，好在作用是一样的。
            const update = () => {
                const modelMatrix = computeCzmModelMatrix({
                    rotation: this.selfRotation,
                });
                if (this._customPrimitiveForCircle)
                    this._customPrimitiveForCircle.localModelMatrix = modelMatrix && Cesium.Matrix4.toArray(modelMatrix) as ESJNativeNumber16 || undefined;
            };
            update();
            this.dispose(this.selfRotationChanged.disposableOn(update));
        }
        {
            const update = () => {
                if (this.customPrimitiveForCircle && this.czmTexture)
                    this.customPrimitiveForCircle.uniformMap = {
                        "u_image": {
                            "type": "texture",
                            "id": this.czmTexture.id,
                        },
                        "u_color": this.color,
                    };
            };
            update();
            this.dispose(this.colorChanged.disposableOn(update));
        }
    }
    private _createDebugAxis(czmViewer: ESCesiumViewer) {
        this._debugAxis = this.ad(createDebugAxis(czmViewer));
        this.dispose(track([this._debugAxis, 'pixelSize'], [this, 'pixelSize']));
        this.dispose(track([this._debugAxis, 'position'], [this, 'position']));
        this.dispose(track([this._debugAxis, 'rotation'], [this, 'rotation']));
        this.dispose(track([this._debugAxis, 'show'], [this, 'debug']));

        {
            const update = () => {
                const modelMatrix = computeCzmModelMatrix({
                    rotation: this.selfRotation,
                });
                if (this._debugAxis)
                    this._debugAxis.localModelMatrix = modelMatrix && Cesium.Matrix4.toArray(modelMatrix) as ESJNativeNumber16 || undefined;
            };
            update();
            this.dispose(this.selfRotationChanged.disposableOn(update));
        }

        {
            const update = () => {
                if (this._debugAxis)
                    this._debugAxis.uniformMap = { u_color: this.color }
            };
            update();
            this.dispose(this.colorChanged.disposableOn(update));
        }
    }
}
export namespace GeoRotatorCircle {
    export const createDefaultProps = () => ({
        // 属性配置
        show: true,
        allowPicking: false,
        editing: false,
        position: reactArrayWithUndefined<[number, number, number]>(undefined),
        rotation: reactArray<[number, number, number]>([0, 0, 0]),
        selfRotation: reactArray<[number, number, number]>([0, 0, 0]),
        pixelSize: 300,
        color: reactArray<[number, number, number, number]>([1, 1, 1, 1]),
        circleRotation: 0,
        circleStartRotation: 0,
        circleEndRotation: 0,
        debug: false,
    });
}
extendClassProps(GeoRotatorCircle.prototype, GeoRotatorCircle.createDefaultProps);
export interface GeoRotatorCircle extends ReactivePropsToNativePropsAndChanged<ReturnType<typeof GeoRotatorCircle.createDefaultProps>> { }

function createCustomPrimitive(czmViewer: ESCesiumViewer) {
    const json = {
        "type": "CzmCustomPrimitive",
        "allowPicking": true,
        "positionEditing": false,
        "pixelSize": 100,
        "boundingVolume": {
            "type": "LocalAxisedBoundingBox",
            "data": {
                "min": [
                    0,
                    -1,
                    0
                ],
                "max": [
                    1,
                    0,
                    0
                ]
            }
        },
        "renderState": {
            "depthTest": {
                "enabled": false
            },
            "cull": {
                "enabled": false,
                "face": 1029
            },
            "depthMask": false,
            "blending": {
                "enabled": true,
                "equationRgb": 32774,
                "equationAlpha": 32774,
                "functionSourceRgb": 770,
                "functionSourceAlpha": 1,
                "functionDestinationRgb": 771,
                "functionDestinationAlpha": 771
            }
        },
        "localPosition": [
            -0.5,
            -0.5,
            0
        ],
        "localRotation": [
            -90,
            0,
            0
        ]
    };
    return createInnerClassFromJson(json, CzmCustomPrimitive, czmViewer);
}
function createDebugAxis(czmViewer: ESCesiumViewer) {
    const json = {
        "type": "CzmCustomPrimitive",
        "allowPicking": true,
        "positionEditing": false,
        "position": [
            114.86128105686282,
            40.86825802653499,
            0
        ],
        "primitiveType": "LINES",
        "vertexShaderSource": "in vec3 position;\nvoid main()\n{\n    // 如果这一句注释，要相应地注释掉attribute中的normal，也就是说顶点属性要和shader中的一一匹配！\n    gl_Position = czm_modelViewProjection * vec4(position, 1.0);\n}\n",
        "fragmentShaderSource": "uniform vec4 u_color;\nvoid main()\n{\n    out_FragColor = u_color;\n}\n",
        "uniformMap": {
            "u_color": [
                1,
                1,
                0,
                1
            ]
        },
        "name": "CzmCustomPrimitive_47fd",
        "attributes": {
            "position": {
                "typedArray": {
                    "type": "Float32Array",
                    "array": [
                        0,
                        0,
                        0,
                        0,
                        0,
                        1
                    ]
                },
                "componentsPerAttribute": 3
            }
        }
    }
    return createInnerClassFromJson(json, CzmCustomPrimitive, czmViewer);
}